/*
grpc 包实现了游戏会话的 gRPC 服务，负责将 gamesession 模块的核心功能（创建会话、管理玩家、控制会话状态）
通过 gRPC 协议暴露给外部客户端（如游戏客户端、管理后台），支持跨进程、跨语言的远程调用。
*/
package grpc

import (
	"context"                // gRPC 方法的上下文，用于超时控制、请求取消等
	"gameServer/gamesession" // 游戏会话管理模块，提供会话的创建、玩家管理等核心逻辑
	"gameServer/proto"       // 编译后的 Protobuf 包，定义了 gRPC 的接口结构和数据类型
	"net"                    // 网络包，用于创建 TCP 监听

	"google.golang.org/grpc"            // gRPC 核心库，提供服务器创建、服务注册等功能
	"google.golang.org/grpc/reflection" // gRPC 反射服务，支持调试工具（如 grpcurl）查看服务接口
)

// sessionServiceServer gRPC 会话服务的实现结构体，
// 嵌入 proto.UnimplementedSessionServiceServer 以保证版本兼容性，
// 持有 gamesession.SessionManager 实例，依赖其实现会话的核心逻辑
type sessionServiceServer struct {
	// 嵌入 Protobuf 生成的空实现：即使后续接口新增方法，未实现时也不会编译报错（兼容性保障）
	proto.UnimplementedSessionServiceServer
	sessionManager *gamesession.SessionManager // 会话管理器实例，核心业务逻辑的实际执行者
}

// NewSessionServiceServer 创建 gRPC 会话服务实例，绑定会话管理器
// 参数：
//
//	sm: 会话管理器实例（外部传入，解耦服务实现与业务逻辑）
//
// 返回值：
//
//	*sessionServiceServer: 初始化后的 gRPC 服务实例
func NewSessionServiceServer(sm *gamesession.SessionManager) *sessionServiceServer {
	return &sessionServiceServer{
		sessionManager: sm, // 绑定会话管理器，后续所有 gRPC 方法通过它调用业务逻辑
	}
}

// CreateSession 实现 Protobuf 定义的 CreateSession gRPC 方法，用于创建游戏会话
// 参数：
//
//	ctx: 上下文对象，可传递超时、取消信号等
//	req: 客户端发送的请求，包含会话ID、最大/最小玩家数等配置
//
// 返回值：
//
//	*proto.CreateSessionResponse: 服务端响应，包含会话创建结果（成功/失败、会话ID、提示信息）
//	error: gRPC 方法的错误（若返回非nil，gRPC 框架会自动封装错误码）
func (s *sessionServiceServer) CreateSession(ctx context.Context, req *proto.CreateSessionRequest) (*proto.CreateSessionResponse, error) {
	// 1. 将 gRPC 请求的参数（proto 类型）转换为 gamesession 模块的 SessionConfig 类型
	config := gamesession.SessionConfig{
		MaxPlayers: int(req.GetMaxPlayer()), // 获取请求中的最大玩家数（proto 中是 int32，转成 int）
		MinPlayers: int(req.GetMinPlayer()), // 获取请求中的最小玩家数
	}

	// 2. 调用会话管理器的 CreateSessionById 方法创建会话（核心业务逻辑）
	session, err := s.sessionManager.CreateSessionById(req.GetSessionId(), config)
	if err != nil {
		// 3. 若创建失败，返回“失败”响应（包含错误信息），gRPC 状态码为 OK（错误信息在响应体中）
		return &proto.CreateSessionResponse{
			Success: false,       // 标识创建失败
			Message: err.Error(), // 携带具体错误信息（如“会话ID已存在”）
		}, nil
	}

	// 4. 若创建成功，返回“成功”响应（包含会话ID和成功提示）
	return &proto.CreateSessionResponse{
		SessionId: session.ID(),                   // 返回创建的会话唯一ID
		Success:   true,                           // 标识创建成功
		Message:   "Session created successfully", // 成功提示信息
	}, nil
}

// GetSession 实现 Protobuf 定义的 GetSession gRPC 方法，用于获取会话详情
// 参数：
//
//	ctx: 上下文对象
//	req: 客户端请求，包含要查询的会话ID
//
// 返回值：
//
//	*proto.GetSessionResponse: 会话详情响应（ID、状态、玩家列表、玩家数量）
//	error: gRPC 错误（若会话不存在，直接返回错误，gRPC 框架会封装为 NOT_FOUND 状态码）
func (s *sessionServiceServer) GetSession(ctx context.Context, req *proto.GetSessionRequest) (*proto.GetSessionResponse, error) {
	// 1. 调用会话管理器获取指定ID的会话
	session, err := s.sessionManager.GetSession(req.GetSessionId())
	if err != nil {
		// 2. 若会话不存在，直接返回错误（gRPC 会自动将其转为 NOT_FOUND 状态码）
		return nil, err
	}

	// 3. 将会话信息（gamesession 类型）转换为 proto 响应类型，返回给客户端
	return &proto.GetSessionResponse{
		SessionId:   session.ID(),                 // 会话ID
		State:       session.State().String(),     // 会话状态（如“StateReady”，转成字符串）
		Players:     session.GetPlayers(),         // 会话中的玩家ID列表
		PlayerCount: int32(session.PlayerCount()), // 玩家数量（int 转 int32，适配 proto 类型）
	}, nil
}

// AddPlayer 实现 Protobuf 定义的 AddPlayer gRPC 方法，用于向会话添加玩家
// 参数：
//
//	ctx: 上下文对象
//	req: 客户端请求，包含会话ID和要添加的玩家ID
//
// 返回值：
//
//	*proto.AddPlayerResponse: 添加结果响应（成功/失败、提示信息）
//	error: gRPC 错误
func (s *sessionServiceServer) AddPlayer(ctx context.Context, req *proto.AddPlayerRequest) (*proto.AddPlayerResponse, error) {
	// 1. 先获取请求中的会话（确保会话存在）
	session, err := s.sessionManager.GetSession(req.GetSessionId())
	if err != nil {
		// 2. 若会话不存在，返回失败响应
		return &proto.AddPlayerResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	// 3. 调用会话的 AddPlayer 方法添加玩家（核心业务逻辑）
	err = session.AddPlayer(req.GetPlayerId())
	if err != nil {
		// 4. 若添加失败（如会话满员、玩家已存在），返回失败响应
		return &proto.AddPlayerResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	// 5. 若添加成功，返回成功响应
	return &proto.AddPlayerResponse{
		Success: true,
		Message: "Player added successfully",
	}, nil
}

// RemovePlayer 实现 Protobuf 定义的 RemovePlayer gRPC 方法，用于从会话移除玩家
// 参数：
//
//	ctx: 上下文对象
//	req: 客户端请求，包含会话ID和要移除的玩家ID
//
// 返回值：
//
//	*proto.RemovePlayerResponse: 移除结果响应
//	error: gRPC 错误
func (s *sessionServiceServer) RemovePlayer(ctx context.Context, req *proto.RemovePlayerRequest) (*proto.RemovePlayerResponse, error) {
	// 1. 获取请求中的会话（确保会话存在）
	session, err := s.sessionManager.GetSession(req.GetSessionId())
	if err != nil {
		return &proto.RemovePlayerResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	// 2. 调用会话的 RemovePlayer 方法移除玩家
	err = session.RemovePlayer(req.GetPlayerId())
	if err != nil {
		// 3. 移除失败（如玩家不在会话中），返回失败响应
		return &proto.RemovePlayerResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	// 4. 移除成功，返回成功响应
	return &proto.RemovePlayerResponse{
		Success: true,
		Message: "Player removed successfully",
	}, nil
}

// StartSession 实现 Protobuf 定义的 StartSession gRPC 方法，用于启动游戏会话
// 参数：
//
//	ctx: 上下文对象
//	req: 客户端请求，包含要启动的会话ID
//
// 返回值：
//
//	*proto.StartSessionResponse: 启动结果响应
//	error: gRPC 错误
func (s *sessionServiceServer) StartSession(ctx context.Context, req *proto.StartSessionRequest) (*proto.StartSessionResponse, error) {
	// 1. 获取请求中的会话
	session, err := s.sessionManager.GetSession(req.GetSessionId())
	if err != nil {
		return &proto.StartSessionResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	// 2. 调用会话的 Start 方法启动会话（如从 StateReady 转为 StatePlaying）
	err = session.Start()
	if err != nil {
		// 3. 启动失败（如会话状态不允许启动），返回失败响应
		return &proto.StartSessionResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	// 4. 启动成功，返回成功响应
	return &proto.StartSessionResponse{
		Success: true,
		Message: "Session started successfully",
	}, nil
}

// EndSession 实现 Protobuf 定义的 EndSession gRPC 方法，用于结束游戏会话
// 参数：
//
//	ctx: 上下文对象
//	req: 客户端请求，包含要结束的会话ID
//
// 返回值：
//
//	*proto.EndSessionResponse: 结束结果响应
//	error: gRPC 错误
func (s *sessionServiceServer) EndSession(ctx context.Context, req *proto.EndSessionRequest) (*proto.EndSessionResponse, error) {
	// 1. 获取请求中的会话
	session, err := s.sessionManager.GetSession(req.GetSessionId())
	if err != nil {
		return &proto.EndSessionResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	// 2. 调用会话的 End 方法结束会话（如从 StatePlaying 转为 StateFinished）
	err = session.End()
	if err != nil {
		// 3. 结束失败（如会话状态不允许结束），返回失败响应
		return &proto.EndSessionResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	// 4. 结束成功，返回成功响应
	return &proto.EndSessionResponse{
		Success: true,
		Message: "Session ended successfully",
	}, nil
}

// StartGrpcServer 启动 gRPC 服务器，监听指定端口并注册会话服务
// 参数：
//
//	port: 服务器监听的端口号（如"50051"）
//	sessionManager: 会话管理器实例（传递给 gRPC 服务，用于业务逻辑调用）
//
// 返回值：
//
//	error: 服务器启动失败时返回错误（如端口被占用），启动成功后会阻塞在 Serve 方法
func StartGrpcServer(port string, sessionManager *gamesession.SessionManager) error {
	// 1. 创建 TCP 监听：监听所有网卡（":"）的指定端口，协议为 TCP
	lis, err := net.Listen("tcp", ":"+port)
	if err != nil {
		return err // 监听失败（如端口被占用），返回错误
	}

	// 2. 创建 gRPC 服务器实例（使用默认配置）
	grpcServer := grpc.NewServer()

	// 3. 注册会话服务：将 sessionServiceServer 实例注册到 gRPC 服务器，
	// 使服务器能处理 proto 中定义的 SessionService 接口的请求
	proto.RegisterSessionServiceServer(grpcServer, NewSessionServiceServer(sessionManager))

	// 4. 注册反射服务：支持 gRPC 调试工具（如 grpcurl、Postman）查看服务列表、接口定义，
	// 仅建议在开发/测试环境启用，生产环境可关闭
	reflection.Register(grpcServer)

	// 5. 启动 gRPC 服务器：阻塞当前 goroutine，持续处理客户端请求，
	// 直到服务器被关闭（如调用 grpcServer.Stop()）
	return grpcServer.Serve(lis)
}
